/**
 * Copyright (C) 2010 D. Zoller, M. Richter, M. Blumenstein & W. Biller
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.siafu.rest.commands;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.List;

import com.googlecode.siafu.rest.commands.parser.DefaultSiafuResultParser;
import com.googlecode.siafu.rest.commands.parser.SiafuResultParser;
import com.googlecode.siafu.rest.exceptions.InternalServerException;
import com.googlecode.siafu.rest.utils.ServerConfig;

import de.nec.nle.siafu.externalCommand.CommandNames;

/**
 * abstract SiafuTCPCommand
 * connects to the siafu service and sends the provided commands {@link #getCommands()}
 * 
 * @author D. Zoller
 * @version $Id: AbstractSiafuTCPCommand.java,v 1.11 2010-06-16 08:34:52 nosebrain Exp $
 * @param <R> the result of the command
 */
public abstract class AbstractSiafuTCPCommand<R> implements SiafuTCPCommand<R> {
	
	/**
	 * all responses are separated by this string
	 */
	public static final String RESPONSE_SPLITTER = "\n";
	
	
	@Override
	public R execute() {
		final StringBuilder result = new StringBuilder();
		final ServerConfig config = ServerConfig.getInstance();
		
		// XXX: cause there are several bugs in siafu we open a socket for each command
		for (final String command : this.getCommands()) {
			try {
				final Socket socket = new Socket();
				socket.connect(new InetSocketAddress(config.getHost(), config.getPort()), 10000);
				socket.setSoTimeout(15000);
				final PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
				final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				
				out.println(command);
				final String commandResult = in.readLine();
				if (commandResult != null) {
					result.append(commandResult.trim()).append(RESPONSE_SPLITTER);
				}
				
				// close all sockets and streams
				in.close();
				out.close();
				socket.close();
			} catch (final SocketTimeoutException e) {
				if (e.getMessage() != null && e.getMessage().endsWith("Read timed out")) {
					if (command.startsWith(CommandNames.HIDE) || command.startsWith(CommandNames.UNHIDE)) {
						continue; // hide and unhide the server response nothing
					}
				}
				
				throw new InternalServerException(e);
			} catch (final IOException e) {
				throw new InternalServerException(e);
			}
		}
	
		return this.parseResult(result.toString().trim());
	}
	
	protected R parseResult(String string) {
		return this.getResultParser().parse(string);
	}
	
	/**
	 * @return the commands to send to the tcp siafu service
	 */
	protected abstract List<String> getCommands();

	/**
	 * @return the resultParser
	 */
	public SiafuResultParser<R> getResultParser() {
		return new DefaultSiafuResultParser<R>();
	}
}
